package pkg.action;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.sourceforge.stripes.action.After;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.validation.ValidationErrors;
import pkg.dao.EntityDAO;
import pkg.entity.Teacher;
import pkg.entity.Auction;
import pkg.entity.Bid;
import pkg.entity.Grade;
import pkg.filter.TeacherFilter;
import pkg.filter.AuctionFilter;
import pkg.filter.BidFilter;

public class ShowAuctionActionBean extends BaseActionBean {

    private Auction auction;
    private List<Auction> auctions;
    private Teacher teacher;
    private Map<Grade, List<Teacher>> gradeMap;
    private List<Teacher> teachers;
    private Bid bid;
    private List<Bid> bids;

    public ShowAuctionActionBean() {
    }

    @DefaultHandler
    public Resolution view() {
        setTeacher(getDefaultTeacher());
        RedirectResolution result = new RedirectResolution(this.getClass());
        result.addParameter("teacher.id", getTeacher().getId());
        result.addParameter("viewTeacher");
        return result;
    }

    public Resolution viewTeacher() {
        loadGradeMap();
        EntityDAO dao = new EntityDAO();
        setTeacher(dao.find(Teacher.class, getTeacher().getId()));

        AuctionFilter f = new AuctionFilter();
        f.setTeacher(getTeacher());

        setAuctions((List<Auction>) dao.findAllByFilter(f));
        removeAuctionsNotVisible();

        if (getAuctions().size() == 1) {
            setAuction(getAuctions().get(0));
            return viewAuction();
        } else {
            return new ForwardResolution("/ShowTeacherAuctions.jsp");
        }
    }

    public Resolution viewAuction() {
        loadGradeMap();
        loadAuction();
        return new ForwardResolution("/ShowAuction.jsp");
    }

    public Resolution doBid() {
        loadGradeMap();
        loadAuction();

        if (getUser() == null) {
            addError("You many not bid. Please register first to bid.");
            return viewAuction();
        }

        if (bid == null || bid.getAmount() == null) {
            addError("bid.amount", "Bid must not be empty.");
            return getContext().getSourcePageResolution();
        }

        Date now = new Date();

        if (auction.isClosed()) {
            addError("Auction is closed.");
            return getContext().getSourcePageResolution();
        }

        if (!auction.isStarted()) {
            addError("Auction has not started.");
            return getContext().getSourcePageResolution();
        }

        if (!auction.isActive()) {
            addError("This auction is no longer active.");
            return getContext().getSourcePageResolution();
        }

        Float minAmount = auction.getAmount() + auction.getIncrement();

        if (bid.getAmount() < minAmount) {
            String msg = MessageFormat.format("Your bid of {0,number,currency} is too low. It must be at least {1,number,currency}.", bid.getAmount(), minAmount);
            addError(msg);
            return getContext().getSourcePageResolution();
        }
        EntityDAO dao = new EntityDAO();

        bid.setTimestamp(new Date());
        bid.setAuctionId(auction.getId());
        bid.setUserId(getUser().getId());
        bid.setActive(true);

        bid = dao.update(bid);
        Bid curBid = auction.getBid();
        if (curBid != null) {
            if (curBid.getTimestamp().compareTo(now) < 0) {
                auction.setAmount(bid.getAmount());
                auction.setBidId(bid.getId());
                auction = dao.update(auction);
            }
        }
        loadBids();

        return new ForwardResolution("/ShowAuction.jsp");
    }

    @After(stages = LifecycleStage.BindingAndValidation)
    public void loadRequestIfErrors() {
        ValidationErrors ve = getContext().getValidationErrors();
        if (ve.size() > 0 && getAuction() != null && getAuction().getId() != null) {
            loadAuction();
        }
        loadLookups();
        loadGradeMap();
    }

    public void loadLookups() {
        setTeachers(new ArrayList<Teacher>());
        EntityDAO dao = new EntityDAO();
        TeacherFilter f = new TeacherFilter();
        getTeachers().addAll(dao.findAllByFilter(f));
    }

    private void loadGradeMap() {
        if (gradeMap != null) {
            return;
        }
        Date lastTeacherTime = (Date) getContext().getServletContext().getAttribute("lastTeacherTime");
        Date currentTeacherTime = (Date) getCache("teacherTime");
        gradeMap = (Map<Grade, List<Teacher>>) getContext().getServletContext().getAttribute("teacherMap");
        if (currentTeacherTime == null || lastTeacherTime == null || gradeMap == null || lastTeacherTime.before(currentTeacherTime)) {
            System.out.println("Reloading grade map");
            EntityDAO dao = new EntityDAO();
            TeacherFilter f = new TeacherFilter();

            List<Teacher> teachers = dao.findAllByFilter(f);
            Collections.sort(teachers, new Comparator<Teacher>() {

                public int compare(Teacher t, Teacher t1) {
                    long result = t.getGrade().getSortOrder().compareTo(t1.getGrade().getSortOrder());
                    if (result == 0) {
                        result = t.getLastName().compareTo(t1.getLastName());
                    }
                    return (int) result;
                }
            });
            gradeMap = new LinkedHashMap<Grade, List<Teacher>>();

            Grade lastGrade = null;
            List<Teacher> gradeList = new ArrayList<Teacher>();

            Comparator<Teacher> c = new Comparator<Teacher>() {

                public int compare(Teacher t, Teacher t1) {
                    return t.getLastName().compareTo(t1.getLastName());
                }
            };

            for (Teacher t : teachers) {
                if (!t.getGrade().equals(lastGrade)) {
                    if (lastGrade != null) {
                        Collections.sort(gradeList, c);
                        gradeMap.put(lastGrade, gradeList);
                        lastGrade = t.getGrade();
                        gradeList = new ArrayList<Teacher>();
                    }
                    lastGrade = t.getGrade();
                }
                gradeList.add(t);
            }
            Collections.sort(gradeList, c);
            gradeMap.put(lastGrade, gradeList);
            getContext().getServletContext().setAttribute("teacherMap", gradeMap);
            lastTeacherTime = new Date();
            getContext().getServletContext().setAttribute("lastTeacherTime", lastTeacherTime);
            if (currentTeacherTime == null) {
                setCache("teacherTime", lastTeacherTime);
            }
        }
    }

    private void loadAuction() {
        AuctionFilter f = new AuctionFilter();
        f.setAuction(getAuction());
        EntityDAO dao = new EntityDAO();
        setAuction((Auction) dao.findOneByFilter(f));
        setTeacher(getAuction().getTeacher());
        loadBids();
        // Make sure current bid on auction is, in fact, the latest bid.
        if (bids.size() > 0) {
            if (auction.getBidId() != bids.get(0).getId()) {
                auction.setBidId(bids.get(0).getId());
                auction.setAmount(bids.get(0).getAmount());
                dao.update(auction);
            }
        }
    }

    private void loadBids() {
        EntityDAO dao = new EntityDAO();
        BidFilter bf = new BidFilter();
        bf.setNumRows(5);
        bf.setAuction(auction);
        bids = dao.findAllByFilter(bf);
    }

    //<editor-fold defaultstate="collapsed" desc=" Setters/Getters ">
    public Auction getAuction() {
        return auction;
    }

    public void setAuction(Auction category) {
        this.auction = category;
    }

    private Teacher getDefaultTeacher() {
        EntityDAO dao = new EntityDAO();
        TeacherFilter f = new TeacherFilter();
        f.setPrimary(true);
        setTeacher((Teacher) dao.findOneByFilter(f));
        return getTeacher();
    }

    public List<Auction> getAuctions() {
        return auctions;
    }

    public void setAuctions(List<Auction> auctions) {
        this.auctions = auctions;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public Map<Grade, List<Teacher>> getGradeMap() {
        return gradeMap;
    }

    public void setGradeMap(Map<Grade, List<Teacher>> gradeMap) {
        this.gradeMap = gradeMap;
    }

    public List<Teacher> getTeachers() {
        return teachers;
    }

    public void setTeachers(List<Teacher> teachers) {
        this.teachers = teachers;
    }

    public Bid getBid() {
        return bid;
    }

    public void setBid(Bid bid) {
        this.bid = bid;
    }

    public List<Bid> getBids() {
        return bids;
    }

    public void setBids(List<Bid> bids) {
        this.bids = bids;
    }

    private void removeAuctionsNotVisible() {
        Iterator<Auction> it = auctions.iterator();

        while (it.hasNext()) {
            Auction a = it.next();
            if (!a.isStarted() || !a.isActive()) {
                it.remove();
            }
        }
    }
    //</editor-fold>
}
